﻿using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using GTI = Gadgeteer.SocketInterfaces;

namespace Gadgeteer.Modules.GHIElectronics
{
    // -- CHANGE FOR MICRO FRAMEWORK 4.2 --
    // If you want to use Serial, SPI, or DaisyLink (which includes GTI.SoftwareI2CBus), you must do a few more steps
    // since these have been moved to separate assemblies for NETMF 4.2 (to reduce the minimum memory footprint of Gadgeteer)
    // 1) add a reference to the assembly (named Gadgeteer.[interfacename])
    // 2) in GadgeteerHardware.xml, uncomment the lines under <Assemblies> so that end user apps using this module also add a reference.

    /// <summary>
    /// A passive infrared module that is used to detect motion in the module's field of view for Microsoft .NET Gadgeteer.
    /// </summary>
    /// <example>
    /// <para>The following example uses a <see cref="Motion_Sensor"/> object to detect motion and raise an event. 
    /// </para>
    /// <code>
    /// using System;
    /// using System.Collections;
    /// using System.Threading;
    /// using Microsoft.SPOT;
    /// using Microsoft.SPOT.Presentation;
    /// using Microsoft.SPOT.Presentation.Controls;
    /// using Microsoft.SPOT.Presentation.Media;
    /// using Microsoft.SPOT.Touch;
    ///
    /// using Gadgeteer.Networking;
    /// using GT = Gadgeteer;
    /// using GTM = Gadgeteer.Modules;
    /// using Gadgeteer.Modules.GHIElectronics;
    ///
    /// namespace TestApp
    /// {
    ///     public partial class Program
    ///     {
    ///         void ProgramStarted()
    ///         {
    ///             motion_Sensor.Motion_Sensed += new Motion_Sensor.Motion_SensorEventHandler(motion_Sensor_Motion_Sensed);
    ///         }
    ///
    ///         void motion_Sensor_Motion_Sensed(Motion_Sensor sender, Motion_Sensor.Motion_SensorState state)
    ///         {
    ///             Debug.Print("Motion Detected!");
    ///         }
    ///     }
    /// }
    /// </code>
    /// </example>
    public class Motion_Sensor : GTM.Module
    {
        private GTI.InterruptInput m_motionDetect;
        // Note: A constructor summary is auto-generated by the doc builder.
        /// <summary></summary>
        /// <param name="socketNumber">The socket that this module is plugged in to.</param>
        public Motion_Sensor(int socketNumber)
        {
            // This finds the Socket instance from the user-specified socket number.  
            // This will generate user-friendly error messages if the socket is invalid.
            // If there is more than one socket on this module, then instead of "null" for the last parameter, 
            // put text that identifies the socket to the user (e.g. "S" if there is a socket type S)
            Socket socket = Socket.GetSocket(socketNumber, true, this, null);

            // This creates an GTI.InterruptInput interface. The interfaces under the GTI namespace provide easy ways to build common modules.
            // This also generates user-friendly error messages automatically, e.g. if the user chooses a socket incompatible with an interrupt input.
            this.m_motionDetect = GTI.InterruptInputFactory.Create(socket, GT.Socket.Pin.Three, GTI.GlitchFilterMode.On, GTI.ResistorMode.PullUp, GTI.InterruptMode.RisingAndFallingEdge, this);

            // This registers a handler for the interrupt event of the interrupt input (which is below)
            this.m_motionDetect.Interrupt += (this._input_Interrupt);
        }

        private void _input_Interrupt(GTI.InterruptInput input, bool value)
        {
            this.OnMotion_SensorEvent(this, value ? Motion_SensorState.Ready : Motion_SensorState.Busy);
        }

        /// <summary>
        /// Gets a value that indicates whether the sensor is still high after detecthing motion.
        /// </summary>
        public bool SensorStillActive
        {
            get
            {
                return this.m_motionDetect.Read();
            }
        }

        /// <summary>
        /// Represents the state of the <see cref="Motion_Sensor"/> object.
        /// </summary>
        public enum Motion_SensorState
        {
            /// <summary>
            /// The state of the sensor is low, which means it is now ready to begin detecting motion again.
            /// </summary>
            Ready = 0,
            /// <summary>
            /// The state of the sensor is high, which means it is not ready to begin detecting motion again.
            /// </summary>
            Busy = 1
        }

        /// <summary>
        /// Represents the delegate that is used to handle the <see cref="Motion_Sensed"/> event.
        /// </summary>
        /// <param name="sender">The <see cref="Motion_Sensor"/> object that raised the event.</param>
        /// <param name="state">The state of the Motion_Sensor</param>
        public delegate void Motion_SensorEventHandler(Motion_Sensor sender, Motion_SensorState state);

        /// <summary>
        /// Raised when the state of <see cref="Motion_Sensor"/> is high.
        /// </summary>
        /// <remarks>
        /// Implement this event handler when you want to provide an action associated with Motion_Sensor activity.
        /// The state of the Motion_Sensor is passed to the <see cref="Motion_SensorEventHandler"/> delegate,
        /// so you can use the same event handler for both Motion_Sensor states.
        /// </remarks>
        public event Motion_SensorEventHandler Motion_Sensed;

        private Motion_SensorEventHandler onMotion_Sensor;

        /// <summary>
        /// Raises the <see cref="Motion_Sensed"/> event.
        /// </summary>
        /// <param name="sender">The <see cref="Motion_Sensor"/> that raised the event.</param>
        /// <param name="Motion_SensorState">The state of the Motion_Sensor.</param>
        protected virtual void OnMotion_SensorEvent(Motion_Sensor sender, Motion_SensorState Motion_SensorState)
        {
            if (this.onMotion_Sensor == null)
            {
                this.onMotion_Sensor = new Motion_SensorEventHandler(this.OnMotion_SensorEvent);
            }

            if (Program.CheckAndInvoke(this.Motion_Sensed, this.onMotion_Sensor, sender, Motion_SensorState))
            {
                switch (Motion_SensorState)
                {
                    case Motion_SensorState.Busy:
                        this.Motion_Sensed(sender, Motion_SensorState);
                        break;
                }
            }
        }
    }
}